package com.lpf.nio;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * 功能描述：
 *      1、多个客户端、一个服务端；
 *      2、实现两个客户端A B 连接服务器，A向服务器发消息后，B收到A发的消息，打印消息来源客户端+消息内容。
 * <p>
 * 服务器端只有一个线程。
 *
 * ServerSocketChannel关注的是连接这个事件
 * SocketChannel关注的是数据的读取事件
 *
 * @author lpf
 * @date 2019/11/27 10:57
 * @version: 1.0
 **/
public class NioServer {

    // 保存客户端的连接（channel）
    private static Map<String, SocketChannel> clientMap = new HashMap<>();

    public static void main(String[] args) throws Exception {

        // 创建channel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);

        // 获取服务端对应的socket对象
        ServerSocket serverSocket = serverSocketChannel.socket();
        // 绑定端口号
        serverSocket.bind(new InetSocketAddress(8899));

        // 创建selector
        Selector selector = Selector.open();
        // 注册channel到selector，并关注相应事件（OP_ACCEPT：服务器端关注连接对象）
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);


        while (true) {

            // 会一直阻塞在这里，一直等到他所关注的事件发生。返回它所关注的事件的数量
            int num = selector.select();

            // 获取selectedKey集合(获取所有的已注册并且已发生的事件（selectKey）)
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            selectionKeys.forEach(selectionKey -> {
                final SocketChannel client;
                try {
                    if (selectionKey.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();

                        // 在这里服务端真正的接受客户端连接
                        client = server.accept();

                        client.configureBlocking(false);
                        client.register(selector, SelectionKey.OP_READ);

                        String key = "[" + UUID.randomUUID().toString() + "]";

                        clientMap.put(key, client);
                    } else if (selectionKey.isReadable()) {

                        // 获取socketChannel对象
                        client = (SocketChannel) selectionKey.channel();

                        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        int readCount = client.read(readBuffer);

                        if (readCount > 0) {
                            readBuffer.flip();
                            Charset charset = StandardCharsets.UTF_8;
                            String receivedMessage = String.valueOf(charset.decode(readBuffer).array());
                            System.out.println(client + ":" + receivedMessage);

                            String senderKey = null;
                            // 找到当前的客户端
                            for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                                if (client == entry.getValue()) {
                                    senderKey = entry.getKey();
                                    break;
                                }
                            }

                            // 把消息发给其他客户端
                            for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                                // 不给自己发
                                if (senderKey != null && senderKey.equals(entry.getKey())) {
                                    continue;
                                }
                                SocketChannel channel = entry.getValue();
                                ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                                writeBuffer.put((senderKey + ":" + receivedMessage).getBytes());
                                writeBuffer.flip();
                                channel.write(writeBuffer);
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

            // 删除本次已处理的事件
            selectionKeys.clear();
        }
    }
}
